using System;
using System.Collections;
using System.IO;
using System.Xml.Serialization;

namespace SQLRunnerLib.Project
{
	/// <summary>
	/// Container for the collection of scripts that need to be executed
	/// in a SQL Server
	/// A project class consists of a list of few default properties and a
	/// collection of folders. A folder may contains other folders and a list
	/// of scripts.
	/// </summary>
	[Serializable, XmlRoot(Namespace="", IsNullable=false)]
	public class SQLProject
	{
		private string _filePath;

	    [XmlArrayItem("Folder", typeof(Folder))]
		public ArrayList Folders = new ArrayList();

		[XmlArrayItem("Extension", typeof(string))]
		public ArrayList Extensions = new ArrayList();

		#region Public Helpers

		/// <summary>
		/// Serializes the SQLProject instance and saves it in the passed
		/// full path file.
		/// </summary>
		/// <param name="fileName">Full path of the file to save the serialized instance</param>
		public void Serialize(string fileName)
		{
			XmlSerializer aSerializer = new XmlSerializer(typeof(SQLProject));
			using(StreamWriter aWriter = new StreamWriter(fileName))
			{
				aSerializer.Serialize(aWriter, this);
				aWriter.Close();
				_filePath = fileName;
			}
		}
		
		/// <summary>
		/// The full path of the file that was used to serialized/deserialized
		/// the current instance
		/// </summary>
		/// <returns>Full path of the file</returns>
		public string FileFullPath() {
			return ( _filePath ?? string.Empty );
		}

		#endregion
		#region Static Helpers

		/// <summary>
		/// Reads a XML file that can be deserialized into a SQLProject instance
		/// </summary>
		/// <param name="fileName">the full file path of the file that
		/// contains the serialised SQLProject instance</param>
		/// <returns></returns>
		public static SQLProject Deserialize(string fileName)
		{
			SQLProject sqlProject;
			XmlSerializer aSerializer = new XmlSerializer(typeof(SQLProject));
            // WI#10777 - SQLRunnerCommand unable to read .sqlrunner file defect
            // Project file cannot be open if file is READ ONLY => Set access to ONLY Read
			using (FileStream aReader = new FileStream(fileName, FileMode.Open, FileAccess.Read))
			{
				sqlProject = (SQLProject) aSerializer.Deserialize(aReader);
				aReader.Close();
				sqlProject._filePath = fileName;
			}
			return sqlProject;
		}

		/// <summary>
		/// Retrieves the list of files in the root of the passed folder. If
		/// the IncludeAllFiles is TRUE then it returns all the files that match
		/// to the project extensions
		/// </summary>
		/// <param name="folder">ProjectFolder full path</param>
		/// <param name="project">Project that contains the passed folder</param>
		/// <returns>List of files to run that where found at the root of the passed folder</returns>
		public static ArrayList FilesInRootFolder(Folder folder, SQLProject project) {
			DirectoryInfo mainDir = new DirectoryInfo(folder.Path);
			// run files before subfolders
			ArrayList files = new ArrayList();
			if (folder.IncludeAllFiles) 
			{
				foreach (string ext in project.Extensions) {
					files.AddRange(mainDir.GetFiles("*." + ext));
				}
			}
			else 
			{
				foreach (string file in folder.Files) {
					FileInfo fileinfo = new FileInfo(Path.Combine(folder.Path, file));
					files.Add(fileinfo);
				}
			}
			return files;
		}

		/// <summary>
		/// All the files contained in a particular folder
		/// </summary>
		/// <param name="folder">ProjectFolder full path</param>
		/// <param name="project">Project that contains the passed folder</param>
		/// <returns>Full path of all the files in the passed folder</returns>
		private static ArrayList FilesInFolder(Folder folder, SQLProject project) {
			ArrayList files = FilesInRootFolder(folder, project);
			if (folder.IncludeAllSubFolders) {
				foreach (Folder subfolder in folder.Folders) {
					files.AddRange(FilesInRootFolder(subfolder, project));
				}
			}
			return files;
		}

		/// <summary>
		/// Obtains the full list of files that a SQLProject contains
		/// </summary>
		/// <param name="project"></param>
		/// <returns>Full path of all the files in the SQLProject</returns>
		public static ArrayList FilesInProject(SQLProject project) {
			ArrayList files = new ArrayList();
			foreach (Folder folder in project.Folders) {
				files.AddRange(FilesInFolder(folder, project));
			}
			return files;
		}

		#endregion

	}
}
